
//
// 110514a- AH - (Chaos) Created.
// 110514b- AH - (Garage) Finished first version.
// 110605 - AH - (Garage) Made table auto size columns again, look like the 
//               right thing to do.
// 110704 - AH - (Garage) Variable height for network component.
// 110805 - AH - Replaced use of ForestConversationComponent with use of
//               ForestFolderConversationComponent.
// 120128 - AH - (Garage) Support for new ProductTable ontology.
//

package org.soekrat.gui.swing.chat.objectconversation;

import static org.soekrat.engine.rethor.RethorUtil.centrifuge;

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Point;
import java.awt.Rectangle;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import javax.swing.AbstractCellEditor;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JViewport;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.TableCellEditor;
import javax.swing.table.TableCellRenderer;

import org.soekrat.AttributeObject;
import org.soekrat.engine.Soekrat2Engine;
import org.soekrat.engine.forest.Tree;
import org.soekrat.engine.network.Cluster;
import org.soekrat.engine.orb.Soekrat2Orb;
import org.soekrat.engine.rethor.RethorUtil;
import org.soekrat.engine.visitor.Lines;
import org.soekrat.gui.swing.chat.objectconversation.forest.ForestFolderConversationComponent;

/**
 * <p>
 *   Panel to dynamically show GUI elements that represents retrieved 
 *   information. 
 * </p>
 * @author Arne Halvorsen (AH)
 * @author Laetitia Le Chatton (LLC) (and this one she loves). 
 */
@SuppressWarnings("serial")
public final class ObjectConversationPanel extends JPanel 
{
  private final Soekrat2Engine m_se;
  
  private static class OcpTableEntry
  {
    final String who;
    final Component comp;
    final Date date;
    
    public OcpTableEntry(String who, Component comp)
    {
      this.who=who;
      this.comp=comp;
      this.date=new Date();
    }
  }

  private static class OcpTableModel extends AbstractTableModel
  {
    private List<OcpTableEntry> m_entries=new ArrayList<OcpTableEntry>();
    
    /**
     * <p>
     *   Constructor.
     * </p>
     */
    public OcpTableModel(){}
    
    /**
     * <p>
     *   Adds a
     *   {@link Component} of the conversation.
     * </p>
     * @param who Name on who said this.
     * @param c   {@code Component} to add.
     */
    public void add(String who, Component c)
    {
      if (c==null)
      {
        throw new NullPointerException("c");
      }
      
      m_entries.add(new OcpTableEntry(who, c));
    }
    
    /**
     * <p>
     *   Gets number of components of the conversation.
     * </p>
     * @return Count.
     */
    public int size(){ return m_entries.size(); }
  
    public int getColumnCount(){ return 3; }
    
    public int getRowCount(){ return m_entries.size(); }

    public Object getValueAt(int row, int col) 
    {
      OcpTableEntry entry=m_entries.get(row);
      switch (col)
      {
        case 0 : return entry.who;
        case 1 : return entry.comp;
        case 2 : return entry.date;
        default : throw new Error();
      }
    }

    public Class<?> getColumnClass(int col)
    {
      switch (col)
      {
        case 0 : return String.class;
        case 1 : return Component.class;
        case 2 : return Date.class;
        default : throw new Error();
      }
    }

    @Override
    public String getColumnName(int col) 
    {
      switch (col)
      {
        case 0 : return "Who";
        case 1 : return "Conversation"; 
        case 2 : return "Time";
        default : throw new Error();
      }
    }

    @Override
    public boolean isCellEditable(int row, int col){ return col==1; }
    
  }
  
  private final OcpTableModel m_model=new OcpTableModel();
  
  private JTable m_table=new JTable(m_model);
  
  /**
   * <p>
   *   The renderer for the component column, simple return the component, 
   *   components know how to render them self :-)
   * </p>
   * @author Arne Halvorsen (AH)
   */
  private final static class ComponentRenderer implements TableCellRenderer
  {
    public Component getTableCellRendererComponent(JTable table, Object value,
      boolean isSelected, boolean hasFocus, int row, int column) 
    {
      return ((Component)value);    
    }
  }
  
  private final static class TimeRenderer implements TableCellRenderer
  {
    private DefaultTableCellRenderer m_dtcr=new DefaultTableCellRenderer();
    
    private SimpleDateFormat m_sdf=new SimpleDateFormat();
    
    TimeRenderer() 
    {
      m_sdf.applyPattern("HH:mm:ss");
    }
    
    public Component getTableCellRendererComponent(JTable table, Object value,
      boolean isSelected, boolean hasFocus, int row, int column)
    {
      String display=m_sdf.format((Date)value);
      return m_dtcr.getTableCellRendererComponent(table, display, isSelected, 
        hasFocus, row, column);
    }
    
  }
  
  private final static class Editor extends AbstractCellEditor 
    implements TableCellEditor
  {
    private Object value;
   
    public Object getCellEditorValue(){ return value; }
 
    public Component getTableCellEditorComponent(JTable table, Object value,
      boolean isSelected, int row, int column) 
    {
      this.value=value;
      return ((Component)value);
    }
    
  }
  
  /**
   * <p>
   *   Constructor.
   * </p>
   */
  public ObjectConversationPanel(Soekrat2Engine se)
  {
    if (se==null)
    {
      throw new NullPointerException("se");
    }
    
    m_se=se;
    
    setLayout(new BorderLayout());
    
    m_table.getColumnModel().getColumn(1).setCellRenderer(
      new ComponentRenderer());
    m_table.getColumnModel().getColumn(1).setCellEditor(new Editor());
    m_table.getColumnModel().getColumn(1).setPreferredWidth(500);
  
    m_table.getColumnModel().getColumn(2).setPreferredWidth(100);
    m_table.getColumnModel().getColumn(2).setCellRenderer(new TimeRenderer());
    
    m_table.setShowHorizontalLines(false);
    add(new JScrollPane(m_table), BorderLayout.CENTER);
  }
  
  /**
   * <p>
   *   Adds 
   *   {@link Component} of the conversation.
   * </p>
   * <p>
   *   Default row height will apply.
   * </p>
   * @param who Who said this.
   * @param c   {@code Component}.
   */
  public void addComponent(String who, Component c){ addComponent(who, c, -1); }
  
  /**
   * <p>
   *   Adds 
   *   {@link Component} of the conversation.
   * </p>
   * @param who        Name on who said this.
   * @param c          {@code Component}.
   * @param prefHeight Row height, if {@code <1} then default row height 
   *                   applies.
   */
  public void addComponent(String who, Component c, int prefHeight)
  {
    if (c==null)
    {
      throw new NullPointerException("c");
    }
    
    int row=m_model.size();
    
    m_model.add(who, c);
    m_model.fireTableRowsInserted(row, row);
    
    if (prefHeight>0)
    {
      m_table.setRowHeight(row, prefHeight);
    }
    
    scroll(row);
  }
  
  /**
   * <p>
   *   Adds
   *   {@link Soekrat2Orb}.
   * </p>
   * @param ce {@link Soekrat2Orb} to add.
   */
  public void add(Soekrat2Orb ce)
  {
    @SuppressWarnings("unchecked")
    List<AttributeObject> swt=
      (List<AttributeObject>)ce.getAttribute(Soekrat2Orb.SW);
    
    if (swt!=null && !swt.isEmpty())
    {
      addFromSpareWheel(ce);
    }
    else
    {
      addFromWheel(ce);
    }
  }
  
  @SuppressWarnings("unchecked")
  private void addFromSpareWheel(Soekrat2Orb ce)
  {
    if (ce==null)
    {
      throw new NullPointerException("lines");
    }
    
    addWhatVisitorSaid(ce);
    
    List<AttributeObject> paint=
      (List<AttributeObject>)ce.getAttribute(Soekrat2Orb.SW);
    
    StringBuilder sb=new StringBuilder();
    
    boolean first=true;
    for (AttributeObject curr : paint) 
    {
      if (!first) sb.append(" ");
      first=false;
      sb.append(curr.getName());
    }
    
    String txt=sb.toString();
    
    PlainTextConversationPanel view=
      new PlainTextConversationPanel(m_se, txt);
    
    addComponent("Soekrat", view, 40);
  }
  
  /**
   * <p>
   *   Adds
   *   {@link Component} of the conversation to represents retrieved 
   *   information. 
   * </p>
   * @param paint Information.
   */
  private void addFromWheel(Soekrat2Orb ce)
  {
    if (ce==null)
    {
      throw new NullPointerException("ce");
    }
    
    @SuppressWarnings("unchecked")
    List<AttributeObject> paint=
      (List<AttributeObject>)ce.getAttribute(Soekrat2Orb.W);
    
    addWhatVisitorSaid(ce);
    
    Class<?> ocass=(Class<?>)ce.getAttribute(Soekrat2Orb.SOURCE_CLASS);
    
    List<AttributeObject> resources=new ArrayList<AttributeObject>();
    List<AttributeObject> trees=new ArrayList<AttributeObject>();
    List<AttributeObject> clusters=new ArrayList<AttributeObject>();
    List<AttributeObject> products=new ArrayList<AttributeObject>();
  
    int n=centrifuge(paint, resources, trees, clusters, products);
    switch (n)
    {
      case 0 : break; // TODO
      
      case 1 :
        if (!resources.isEmpty())
        {
          addFromMine(resources);
        }
        else if (!trees.isEmpty())
        {
          addFromForest(trees);
        }
        else if (!clusters.isEmpty())
        {
          addFromNetwork(clusters);
        }
        else if (!products.isEmpty())
        {
          addFromProductTable(products);
        }
      break;
      
      default : break; // TODO
    }
  }
  
  private void addFromForest(List<AttributeObject> painted)
  {
    Tree tree=RethorUtil.buildRetrievedTree(painted);
    
    ForestFolderConversationComponent view=
      new ForestFolderConversationComponent(m_se, tree);
    addComponent("Soekrat", view, 150);
  }
  
  private void addFromMine(List<AttributeObject> painted)
  {
    MineConversationComponent view=new MineConversationComponent(m_se, painted);
    addComponent("Soekrat", view, 40);
  }
  
  private void addFromNetwork(List<AttributeObject> painted)
  {
    for (AttributeObject curr : painted) 
    {
      NetworkConversationComponent view=new NetworkConversationComponent(m_se,
        (Cluster)curr);
      Dimension d=view.getPreferredSize();
      addComponent("Soekrat", view, d.height);
    }
  }
  
  private void addFromProductTable(List<AttributeObject> painted)
  {
    ProductTableConversationComponent view=
      new ProductTableConversationComponent(m_se, painted);
    addComponent("Soekrat", view, 40);
  }
  
  private void addWhatVisitorSaid(Soekrat2Orb ce)
  {
    Lines lines=(Lines)ce.getAttribute(Soekrat2Orb.CHAT_INPUT);
    
    String txt=lines.toString();
    
    PlainTextConversationPanel view=
      new PlainTextConversationPanel(m_se, txt);
    
    addComponent("Visitor", view, 40);
  }
  
  private void scroll(int row)
  {
    Container c=m_table.getParent();
    if (c instanceof JViewport)
    {
      JViewport vp=(JViewport)c;
      
      Rectangle r=m_table.getCellRect(row, 0, true);
      
      Point pt=vp.getViewPosition();
      
      r.setLocation(r.x-pt.x, r.y-pt.y);
      
      vp.scrollRectToVisible(r);
    }
  }
  
}
